#include "ringbuffer/ring_buffer.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/**
 * @brief Expands the ring buffer to a new capacity.
 * @param rb Pointer to the ring buffer structure.
 * @param new_capacity The new desired capacity.
 * @return 0 on success, -1 on failure.
 */
static int ringbuf_expand(ring_buffer_t *rb, size_t new_capacity) {
    uint8_t *new_buf = realloc(rb->buffer, new_capacity);
    if (!new_buf) return -1;

    // Handle data migration for circular buffer
    if (rb->tail < rb->head) {
        const size_t data_after_head = rb->capacity - rb->head;
        memcpy(new_buf + rb->head + (new_capacity - rb->capacity),
               new_buf + rb->head,
               data_after_head);
        rb->head += new_capacity - rb->capacity;
    }

    rb->buffer = new_buf;
    rb->capacity = new_capacity;
    return 0;
}

/**
 * @brief Initializes a fixed-size ring buffer.
 * @param rb Pointer to the ring buffer structure.
 * @param size The initial capacity of the buffer.
 * @return 0 on success, -1 on failure.
 */
int ringbuf_init(ring_buffer_t *rb, size_t size) {
    rb->buffer = malloc(size);
    if (!rb->buffer) return -1;
    rb->head = rb->tail = 0;
    rb->capacity = size;
    rb->dynamic = false;
    return 0;
}

/**
 * @brief Initializes a dynamically growing ring buffer.
 * @param rb Pointer to the ring buffer structure.
 * @param size The initial capacity of the buffer.
 * @return 0 on success, -1 on failure.
 */
int ringbuf_init_dynamic(ring_buffer_t *rb, size_t size) {
    if (ringbuf_init(rb, size) != 0) return -1;
    rb->dynamic = true;
    return 0;
}

/**
 * @brief Frees the ring buffer.
 * @param rb Pointer to the ring buffer structure.
 */
void ringbuf_free(ring_buffer_t *rb) {
    free(rb->buffer);
    rb->capacity = 0;
    rb->head = rb->tail = 0;
}

/**
 * @brief Clears all data from the ring buffer.
 * @param rb Pointer to the ring buffer structure.
 */
void ringbuf_clear(ring_buffer_t *rb) {
    rb->head = rb->tail = 0;
}

/**
 * @brief Writes data into the ring buffer.
 * @param rb Pointer to the ring buffer structure.
 * @param data Pointer to the data to be written.
 * @param len Number of bytes to write.
 * @return Number of bytes actually written.
 */
size_t ringbuf_write(ring_buffer_t *rb, const uint8_t *data, size_t len) {
    if (len == 0) return 0;

    size_t free_space = ringbuf_free_space(rb);
    
    // Expand buffer if needed
    if (rb->dynamic && (len > free_space)) {
        size_t new_capacity = rb->capacity;
        while (new_capacity - ringbuf_used_space(rb) - 1 < len) {
            new_capacity *= 2;
        }
        if (ringbuf_expand(rb, new_capacity) != 0) {
            return 0; // Expansion failed
        }
        free_space = ringbuf_free_space(rb);
    }

    const size_t to_write = (len > free_space && !rb->dynamic) ? free_space : len;
    const size_t first_chunk = rb->capacity - rb->tail;

    if (to_write <= first_chunk) {
        memcpy(rb->buffer + rb->tail, data, to_write);
        rb->tail += to_write;
    } else {
        memcpy(rb->buffer + rb->tail, data, first_chunk);
        memcpy(rb->buffer, data + first_chunk, to_write - first_chunk);
        rb->tail = to_write - first_chunk;
    }

    if (rb->tail >= rb->capacity) rb->tail -= rb->capacity;
    return to_write;
}

/**
 * @brief Reads data from the ring buffer.
 * @param rb Pointer to the ring buffer structure.
 * @param data Pointer to buffer where read data will be stored.
 * @param len Number of bytes to read.
 * @return Number of bytes actually read.
 */
size_t ringbuf_read(ring_buffer_t *rb, uint8_t *data, size_t len) {
    const size_t used = ringbuf_used_space(rb);
    const size_t to_read = (len > used) ? used : len;

    if (to_read == 0) return 0;

    const size_t first_chunk = rb->capacity - rb->head;

    if (to_read <= first_chunk) {
        memcpy(data, rb->buffer + rb->head, to_read);
        rb->head += to_read;
    } else {
        memcpy(data, rb->buffer + rb->head, first_chunk);
        memcpy(data + first_chunk, rb->buffer, to_read - first_chunk);
        rb->head = to_read - first_chunk;
    }

    if (rb->head >= rb->capacity) rb->head -= rb->capacity;
    return to_read;
}

/**
 * @brief Returns the amount of used space in the buffer.
 * @param rb Pointer to the ring buffer structure.
 * @return Number of bytes currently stored in the buffer.
 */
size_t ringbuf_used_space(const ring_buffer_t *rb) {
    return (rb->tail >= rb->head) ? (rb->tail - rb->head) : (rb->capacity - (rb->head - rb->tail));
}

/**
 * @brief Returns the amount of free space in the buffer.
 * @param rb Pointer to the ring buffer structure.
 * @return Number of available bytes in the buffer.
 */
size_t ringbuf_free_space(const ring_buffer_t *rb) {
    return rb->capacity - ringbuf_used_space(rb) - 1; // Leave one byte to differentiate full/empty states
}

/**
 * @brief Checks if the ring buffer is empty.
 * @param rb Pointer to the ring buffer structure.
 * @return true if empty, false otherwise.
 */
bool ringbuf_is_empty(const ring_buffer_t *rb) {
    return rb->head == rb->tail;
}

/**
 * @brief Checks if the ring buffer is full.
 * @param rb Pointer to the ring buffer structure.
 * @return true if full, false otherwise.
 */
bool ringbuf_is_full(const ring_buffer_t *rb) {
    return ringbuf_free_space(rb) == 0 && !rb->dynamic;
}
